package com.nsxy.manager.common.config;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.nsxy.manager.common.utils.PasswordCredentialsMatcher;
import com.nsxy.manager.common.utils.ShiroRealm;

/**
 * Shiro配置
 * @author 潜行的青衣
 * @see
 */
@Order(40)
@Configuration
@PropertySource(value = {
	"classpath:shiro.properties"
})
public class ShiroConfig implements EnvironmentAware, BeanFactoryPostProcessor {
	
	private Environment env;
	
	private net.sf.ehcache.CacheManager cacheManager;
	
	@Bean
	public Realm realm() {
		return new ShiroRealm(shiroEhCacheManager(), this.credentialsMatcher());
	}
	
	@Bean
	public CredentialsMatcher credentialsMatcher() {
		return new PasswordCredentialsMatcher(passwordEncoder());
	}
	
	@Bean
	public BCryptPasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	public CacheManager shiroEhCacheManager() {
		EhCacheManager ehCacheManager = new EhCacheManager();
		ehCacheManager.setCacheManager(cacheManager);
		return ehCacheManager;
	}
	
	@Bean
	public org.apache.shiro.mgt.SecurityManager securityManager() {
		DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
		manager.setCacheManager(this.shiroEhCacheManager());
		return manager;
	}
	
	@Bean
	public MethodInvokingFactoryBean methodInvokingFactoryBean() throws Exception {
		MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
		methodInvokingFactoryBean.setStaticMethod(env.getProperty("shiro.static.method"));
		methodInvokingFactoryBean.setArguments(new Object[]{this.securityManager()});
		methodInvokingFactoryBean.afterPropertiesSet();
		return methodInvokingFactoryBean;
	}
	
	@Bean
	public ShiroFilterFactoryBean shiroFilter() {
		ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
		shiroFilter.setSecurityManager(this.securityManager());
		shiroFilter.setLoginUrl(env.getProperty("shiro.login.url"));
		shiroFilter.setSuccessUrl(env.getProperty("shiro.success.url"));
		shiroFilter.setUnauthorizedUrl(env.getProperty("shiro.unauthorized.url"));
		String chainStr = StringUtils.trimToEmpty(env.getProperty("shiro.filterchains"));
		if(StringUtils.isNotEmpty(chainStr)) {
			String[] chains = StringUtils.split(chainStr, "\\|");
			StringBuilder definitions = new StringBuilder();
			for(int i=0; i<chains.length; i++) {
				definitions.append(chains[i]).append("\n");
			}
			shiroFilter.setFilterChainDefinitions(definitions.toString());
		}
		return shiroFilter;
	}
	
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(this.securityManager());
		authorizationAttributeSourceAdvisor.setOrder(15);
		return authorizationAttributeSourceAdvisor;
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.context.EnvironmentAware#setEnvironment(org.springframework.core.env.Environment)
	 */
	@Override
	public void setEnvironment(Environment environment) {
		env = environment;
	}

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
	 */
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		if(beanFactory.containsBean("ehcacheManagerFactory")) {
			cacheManager = beanFactory.getBean("ehcacheManagerFactory", net.sf.ehcache.CacheManager.class);
		}
	}

}
